package com.gxar.quick.ar.server.db.service;

import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.gxar.common.entity.PageResult;
import com.gxar.common.entity.Result;
import com.gxar.common.entity.login.UserProfile;
import com.gxar.common.exception.APIRuntimeException;
import com.gxar.common.utils.AssemblerUtils2;
import com.gxar.common.utils.PageUtils;
import com.gxar.common.utils.StringUtils;
import com.gxar.quick.ar.server.base.common.properties.MaterialProperties;
import com.gxar.quick.ar.server.base.common.utils.ImageUtils;
import com.gxar.quick.ar.server.base.model.ResultCodeEnum;
import com.gxar.quick.ar.server.base.model.material.info.UploadResultInfo;
import com.gxar.quick.ar.server.base.model.material.mapper.MaterialMapStruct;
import com.gxar.quick.ar.server.base.model.material.vo.request.AdminUpdateMaterialRequest;
import com.gxar.quick.ar.server.base.model.material.vo.request.AdminUploadMaterialRequest;
import com.gxar.quick.ar.server.base.model.material.vo.response.*;
import com.gxar.quick.ar.server.db.mapper.MaterialMapper;
import com.gxar.quick.ar.server.db.model.MaterialsEntity;
import com.gxar.quick.ar.server.db.repository.MaterialRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.NodeList;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author nice
 * @version 1.0
 * @created 2023/11/21.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MaterialService {

    private final MaterialMapper materialMapper;

    private final MaterialRepository materialRepository;

    private final MaterialProperties materialProperties;

    private static final Map<String, MaterialGifResponse> materialResponseMap = new ConcurrentHashMap<>();

    public List<MaterialListResponse> list(Integer pageNow, Integer pageSize, Integer type) {
        pageNow = pageNow == null ? 1 : pageNow;
        pageSize = pageSize == null ? 10 : pageSize;
        PageHelper.startPage(pageNow, pageSize);
        List<MaterialsEntity> materialsEntities = materialMapper.queryPage(type);
        if (CollectionUtils.isEmpty(materialsEntities)) {
            return Collections.emptyList();
        }
        return AssemblerUtils2.assembleList2NewList(materialsEntities, MaterialListResponse.class);
    }

    public UploadResultInfo textToImage(String text, Integer fontSize, String token, String userId, Long materialId,
            Integer fontStyle) {
        //step1: 下载字体
        Optional<MaterialsEntity> materialsEntityOptional = materialRepository.findById(materialId);
        if (!materialsEntityOptional.isPresent()) {
            log.warn("MaterialsEntity is null, id : {}", materialId);
            return null;
        }
        //step2: 读取字体配置
        MaterialsEntity materialsEntity = materialsEntityOptional.get();
        BufferedImage quickAr = ImageUtils.toImage(text, 10, fontStyle, fontSize, materialsEntity.getName());
        File file = new File("ArtWord" + userId + ".png");
        try {
            //step3: 上传图片
            ImageIO.write(quickAr, "png", file);
            String url = materialProperties.getLinkUrl();
            Response response = uploadFile(url, token, userId, file);
            JSONObject object = JSON.parseObject(response.body().string());
            return object.getObject("data", UploadResultInfo.class);
        } catch (IOException e) {
            log.error("Upload material failed! id : {}, user : {}", materialId, userId, e);
        } finally {
            file.delete();
        }
        return null;
    }

    public void uploadFiles(String path, Integer type, Boolean thumbnail) {
        String apiEndpoint = "https://api.gxar.cn/file/api/file/upload";
        String folderPath = "/Users/gxar/Downloads/清水岩新年祝福/" + path;
        String userId = "83";
        String token = "87418191E40A3412FD1691D0B19774C6E5C5B32D8DACFF6D2B983C7F7D257FE1";
        File folder = new File(folderPath);
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (!file.isFile()) {
                    continue;
                }
                try {
                    Response response = uploadFile(apiEndpoint, token, userId, file);
                    JSONObject object = JSON.parseObject(response.body().string());
                    String name = file.getName();
                    List<String> splitPost = StringUtils.splitToList(name, ".");
                    List<String> strings = StringUtils.splitToList(splitPost.get(0), "@");
                    UploadResultInfo data = object.getObject("data", UploadResultInfo.class);
                    MaterialsEntity materialsEntity = getMaterialsEntity(type, data, strings.get(0), thumbnail);
                    materialRepository.save(materialsEntity);
                } catch (IOException e) {
                    log.warn("upload material failed: {}", file.getName());
                }
            }
        }
    }

    @NotNull
    private MaterialsEntity getMaterialsEntity(Integer type, UploadResultInfo data, String name, Boolean thumbnail) {
        MaterialsEntity exist = materialMapper.queryByName(name);
        String url = data.getUrl();
        try {
            url = URLDecoder.decode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if (exist == null) {
            MaterialsEntity materialsEntity = new MaterialsEntity();
            if (thumbnail) {
                materialsEntity.setImageUrl(url);
            } else {
                materialsEntity.setLinkUrl(url);
            }
            materialsEntity.setName(name);
            materialsEntity.setCreator("system");
            materialsEntity.setCreatedAt(new Date());
            materialsEntity.setOperator("system");
            materialsEntity.setUpdatedAt(materialsEntity.getCreatedAt());
            materialsEntity.setType(type);
            return materialsEntity;
        } else {
            if (thumbnail) {
                exist.setImageUrl(url);
            } else {
                exist.setLinkUrl(url);
            }
            return exist;
        }
    }

    private Response uploadFile(String url, String token, String userId, File file) {
        try {
            HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
            httpBuilder.addQueryParameter("product", "quick-ar");
            httpBuilder.addQueryParameter("publics", "true");
            OkHttpClient client = new OkHttpClient();
            RequestBody formBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
                    .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("text/plain"), file))
                    .build();
            Request request = new Request.Builder().url(httpBuilder.build().url()).header("token", token)
                    .addHeader("user_id", userId).post(formBody).build();
            Response response = client.newCall(request).execute();
            return response;
        } catch (IOException e) {
            log.error("Upload file failed! id : {}, user : {}", userId, e);
            throw new APIRuntimeException(e);
        }
    }

    public MaterialGifResponse gifToSprite(String fileUrl, String token, String userId, MultipartFile file) {
        if (file != null) {
            try {
                InputStream inputStream = file.getInputStream();
                return convertGifToSprite(inputStream, token, userId);
            } catch (IOException e) {
                log.warn("MultipartFile gifToSprite exception:{}", e);
            }
        } else {
            //优先取缓存
            MaterialGifResponse materialGifResponse = materialResponseMap.get(fileUrl);
            if (materialGifResponse != null) {
                return materialGifResponse;
            }
            HttpResponse response = HttpUtil.createGet(fileUrl).timeout(60000).execute();
            try {
                MaterialGifResponse gifToSprite = convertGifToSprite(response.bodyStream(), token, userId);
                materialResponseMap.put(fileUrl, gifToSprite);
                return gifToSprite;
            } catch (IOException e) {
                log.warn("Download gifToSprite exception:{}", e);
            }
        }
        return new MaterialGifResponse();
    }

    private MaterialGifResponse convertGifToSprite(InputStream in, String token, String userId) throws IOException {
        MaterialGifResponse response = new MaterialGifResponse();
        ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next();
        ImageInputStream iis = ImageIO.createImageInputStream(in);
        reader.setInput(iis);
        int numFrames = reader.getNumImages(true);
        BufferedImage[] frames = new BufferedImage[numFrames];
        //计算延迟
        int totalDelay = 0;
        for (int i = 0; i < numFrames; i++) {
            totalDelay += getDelayForFrame(reader, i);
        }
        response.setDuring(totalDelay / 100.0);
        // 计算适合的行数和列数
        int columns = (int) Math.ceil(Math.sqrt(numFrames));
        int rows = (int) Math.ceil((double) numFrames / columns);
        response.setColumnCount(columns);
        response.setRowCount(rows);
        for (int i = 0; i < numFrames; i++) {
            frames[i] = reader.read(i);
        }
        int frameWidth = frames[0].getWidth();
        int frameHeight = frames[0].getHeight();
        // 计算适合的间隔
        int interval = Math.max(1, Math.min(frameWidth, frameHeight) / 10);
        int spriteWidth = frameWidth * columns + (columns - 1) * interval;
        int spriteHeight = frameHeight * rows + (rows - 1) * interval;
        BufferedImage spriteImage = new BufferedImage(spriteWidth, spriteHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = spriteImage.createGraphics();
        int frameIndex = 0;
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < columns; col++) {
                if (frameIndex >= numFrames) {
                    break;
                }
                int x = col * (frameWidth + interval);
                int y = row * (frameHeight + interval);
                g2d.drawImage(frames[frameIndex], x, y, null);
                frameIndex++;
            }
        }
        g2d.dispose();
        //上传雪碧图
        File file = new File("GifToSprite" + userId + ".png");
        try {
            ImageIO.write(spriteImage, "png", file);
            String url = materialProperties.getLinkUrl();
            Response uploadFile = uploadFile(url, token, userId, file);
            JSONObject object = JSON.parseObject(uploadFile.body().string());
            UploadResultInfo data = object.getObject("data", UploadResultInfo.class);
            response.setSpriteImgUrl(data.getUrl());
        } catch (Exception e) {
            log.warn("convertGifToSprite exception:{}", e);
        } finally {
            file.delete();
        }
        if (spriteHeight > 5000 || spriteWidth > 5000) {
            log.warn("Sprite image over size. height:{}, width:{}, url:{}", spriteHeight, spriteWidth,
                    response.getSpriteImgUrl());
        }
        return response;
    }

    private int getDelayForFrame(ImageReader reader, int frameIndex) {
        IIOMetadata metadata = null;
        try {
            metadata = reader.getImageMetadata(frameIndex);
            String metaFormatName = metadata.getNativeMetadataFormatName();
            IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(metaFormatName);
            // Locate the delay node
            NodeList childNodes = root.getElementsByTagName("GraphicControlExtension");
            if (childNodes.getLength() > 0) {
                IIOMetadataNode gceNode = (IIOMetadataNode) childNodes.item(0);
                String delayAttr = gceNode.getAttribute("delayTime");
                return Integer.parseInt(delayAttr);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0; // Default delay if unable to retrieve
    }

    public Integer resetMaterialResponseMap() {
        Integer result = materialResponseMap.size();
        materialResponseMap.clear();
        log.info("Clear materialResponseMap {} to {}", result, materialResponseMap.size());
        return result;
    }

    /**
     * 上传素材
     *
     * @param userProfile 用户信息
     * @param request     请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public AdminUploadMaterialResponse uploadMaterial(UserProfile userProfile, AdminUploadMaterialRequest request) {
        MaterialsEntity materialsEntity = materialMapper.queryByName(request.getName());
        if (Objects.nonNull(materialsEntity)) {
            throw new APIRuntimeException(ResultCodeEnum.MATERIAL_NAME_USED);
        }
        MaterialsEntity materials = MaterialMapStruct.INSTANCE.uploadToEntity(request);
        materials.setCreatedAt(new Date());
        materials.setCreator(userProfile.getMobile());
        materials.setUpdatedAt(new Date());
        MaterialsEntity saved = materialRepository.save(materials);
        return MaterialMapStruct.INSTANCE.materialToUpload(saved);
    }

    /**
     * 更新素材
     *
     * @param userProfile 用户信息
     * @param id          素材id
     * @param request     请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public AdminUpdateMaterialResponse updateMaterial(UserProfile userProfile, Long id, AdminUpdateMaterialRequest request) {
        Optional<MaterialsEntity> optionalMaterialsEntity = materialRepository.findById(id);
        if (!optionalMaterialsEntity.isPresent()) {
            throw new APIRuntimeException(ResultCodeEnum.MATERIAL_NOT_EXIST);
        }
        MaterialsEntity entity = optionalMaterialsEntity.get();
        if (!entity.getName().equals(request.getName())) {
            // 校验素材名称是否重复
            MaterialsEntity materialsEntity = materialMapper.queryByName(request.getName());
            if (Objects.nonNull(materialsEntity)) {
                throw new APIRuntimeException(ResultCodeEnum.MATERIAL_NAME_USED);
            }
        }
        MaterialsEntity materials = MaterialMapStruct.INSTANCE.updateToEntity(request);
        materials.setUpdatedAt(new Date());
        materials.setCreatedAt(entity.getCreatedAt());
        materials.setCreator(entity.getCreator());
        materials.setOperator(userProfile.getMobile());
        materials.setId(optionalMaterialsEntity.get().getId());
        MaterialsEntity updated = materialRepository.save(materials);
        return MaterialMapStruct.INSTANCE.materialToUpdate(updated);
    }

    /**
     * 删除素材
     *
     * @param userProfile 用户信息
     * @param id          素材id
     */
    @Transactional(rollbackFor = Throwable.class)
    public void deleteMaterial(UserProfile userProfile, Long id) {
        Optional<MaterialsEntity> optionalMaterialsEntity = materialRepository.findById(id);
        if (!optionalMaterialsEntity.isPresent()) {
            throw new APIRuntimeException(ResultCodeEnum.MATERIAL_NOT_EXIST);
        }
        materialRepository.deleteById(id);
    }

    /**
     * 素材详情
     *
     * @param userProfile 用户信息
     * @param id          素材id
     * @return 响应
     */
    public AdminMaterialDetailResponse materialDetail(UserProfile userProfile, Long id) {
        Optional<MaterialsEntity> optionalMaterialsEntity = materialRepository.findById(id);
        if (optionalMaterialsEntity.isPresent()) {
            return MaterialMapStruct.INSTANCE.materialToDetail(optionalMaterialsEntity.get());
        }
        return new AdminMaterialDetailResponse();
    }

    /**
     * 管理员分页查询素材
     *
     * @param pageNum  当前页
     * @param pageSize 页记录数
     * @param type     类型
     * @return 响应
     */
    public Result<PageResult<AdminMaterialPageListResponse>> pageList(Integer pageNum, Integer pageSize, int type) {
        Page<Object> page = PageHelper.startPage(pageNum, pageSize);
        List<MaterialsEntity> materialsEntities = materialMapper.queryPage(type);
        if (CollectionUtils.isEmpty(materialsEntities)) {
            return Result.success(new PageResult<>());
        }
        List<AdminMaterialPageListResponse> materialPageList = MaterialMapStruct.INSTANCE.entitiesToPageResponse(materialsEntities);
        PageInfo<AdminMaterialPageListResponse> materialPageInfo = new PageInfo<>(materialPageList);
        materialPageInfo.setPageNum(pageNum);
        materialPageInfo.setTotal(page.getTotal());
        return PageUtils.getPageResult(materialPageInfo, materialPageList);
    }

}
